package gp02.uniandes.dialgo.mundo;

import gp02.uniandes.dialgo.IO.Lector;

import java.util.ArrayList;
import java.util.Iterator;

public class RedDeTransporte
{

    /**
     * Atributo que modela el itinerario de la ruta
     */
    private ItinerarioRuta[] rutas;
    /**
     * Atributo que modela las ciudades existentes en la red de transporte
     */
    private Ciudad[] ciudades;
    // ---------------------------------------------------------------------------
    // CONSTRUCTOR
    // ---------------------------------------------------------------------------
    /**
     * Constructor de la clase por defecto sin paramentros
     */
    public RedDeTransporte( )
    {

    }
    /**
     * Constructor de la clase por dia, salidaRuta e itinerario
     * @param dias Los dias de inicio de las diferentes rutas
     * @param salidaRuta la salida inicial de la ruta
     * @param itinerario El itinerario de la ruta
     */
    public RedDeTransporte( int[] dias, Salida salidaRuta, ItinerarioRuta[] rutas )
    {
        for( int i = 0; i < rutas.length; i++ )
        {
            rutas[ i ].setDia( getDia( dias[ i ] ) );
        }
        this.rutas = rutas;
    }
    // ---------------------------------------------------------------------------
    // METODOS
    // ---------------------------------------------------------------------------
    /**
     * Metodo que retorna el dia segun su codigo
     * @param dia El codigo del dia
     * @return El dia segun su codigo
     */
    public static DiasSemana getDia( int dia )
    {
        switch( dia )
        {
            case 0:
                return DiasSemana.Dom;
            case 1:
                return DiasSemana.Lu;
            case 2:
                return DiasSemana.Ma;
            case 3:
                return DiasSemana.Mi;
            case 4:
                return DiasSemana.Ju;
            case 5:
                return DiasSemana.Vi;
            case 6:
                return DiasSemana.Sa;
        }

        return null;
    }
    /**
     * Metodo que asigna las rutas propuestas para la red de transporte
     * @param rutas Las rutas propuestas
     */
    public void setRutas( ItinerarioRuta[] rutas )
    {
        this.rutas = rutas;
    }
    /**
     * Metodo que asigna las cuidades existentes de la red de transporte
     * @param cuidades Las ciudades existentes de la red de transporte
     */
    public void setCuidades( Ciudad[] cuidades )
    {
        this.ciudades = cuidades;
    }
    /**
     * Metodo que retorna el primer itinerario que cumple con la ciudad de origen y la ciudad destino, saliendo a la hora base determinada
     * @param origen La ciudad origen de la ruta propuesta
     * @param destino La ciudad destino de la ruta propuesta
     * @param horaBase La hora base de salida de la ruta propuesta
     * @return actual El primer itinerario que cumple las condiciones, null en caso que no encuentre solucion
     */
    public ItinerarioRuta getPrimerSolucion( Ciudad origen, Ciudad destino, String horaBase )
    {
        if( origen.compareTo( destino ) == 0 )
            return null;

        for( int i = 0; i < rutas.length; i++ )
        {
            ItinerarioRuta actual = rutas[ i ];
            Ciudad origenActual = actual.getCiudadOrigen( );
            Ciudad destinoActual = actual.getCiudadDestino( );

            if( origenActual.compareTo( origen ) == 0 && destinoActual.compareTo( destino ) == 0 )
            {
                if( origenActual.existeHoraBase( horaBase ) )
                    return actual;
            }
        }
        return null;
    }
    /**
     * Metodo que retorna la mejor ruta de todas las posibles.
     * @param origen La ciudad origen de la ruta propuesta
     * @param destino La ciudad destino de la ruta propuesta
     * @param horaBase La hora base de salida de la ruta propuesta
     * @return solucion El itinerario con el menor tiempo posible. Null en caso que no haya solucion
     */
    public ItinerarioRuta getMejorSolucion( Ciudad origen, Ciudad destino, String horaBase )
    {
        if( origen.compareTo( destino ) == 0 )
            return null;

        ItinerarioRuta mejorSolucion = new ItinerarioRuta( );

        for( int i = 0; i < rutas.length; i++ )
        {
            ItinerarioRuta actual = rutas[ i ];
            int tActual = actual.getTiempoTotal( );

            if( i + 1 <= rutas.length )
            {
                ItinerarioRuta siguiente = rutas[ i + 1 ];
                int tSiguiente = siguiente.getTiempoTotal( );

                if( tActual < tSiguiente )
                    mejorSolucion = actual;
                else if( tActual > tSiguiente )
                    mejorSolucion = siguiente;
                else if( tActual == tSiguiente )
                    mejorSolucion = actual;
            }
        }
        return mejorSolucion;
    }
    /**
     * Metodo que retorna todas las rutas que inicien el dia con codigoDia
     * <b>ctx: </b> El dia es un numero entero entre 0 y 6, y los elementos de la lista de rutas pertenecen a ItinerarioRuta
     * <b>post: </b> Se agregaron las rutas validas a la lista solucion, y se retorno esta lista
     * @param codigoDia El codigo del dia de inicio de la ruta
     * @param rutasActuales Las rutas establecidas en la red de transporte
     * @return solucion La lista con todas las rutas que inicien el dia codigoDia
     */
    public ArrayList<ItinerarioRuta> buscarRutasPorDia( int codigoDia, ItinerarioRuta[] rutasActuales )
    {
        this.rutas = rutasActuales;
        ArrayList<ItinerarioRuta> solucion = new ArrayList<ItinerarioRuta>( );
        for( int i = 0; i < rutas.length; i++ )
        {
            if( rutas[ i ].getDia( ) == getDia( codigoDia ) )
                solucion.add( rutas[ i ] );
        }
        return solucion;
    }
    /**
     * Metodo que retorna todas las rutas que inicien en la ciudad con codigoOrigen y terminen en la ciudad con codigoDestino
     * <b> ctx: </b> codigoDestino es un numero natural. Los elementos de la lista de rutas pertenecen a ItinerarioRuta
     * <b> pre: </b> destino pertence a Ciudad
     * <b> post: </b> Se agregaron las rutas validas a la lista solucion, y se retorno esta lista
     * @param codigoDestino El codigo de la ciudad destino
     * @param rutasActuales Las rutas establecidas en la red de transporte
     * @return solucion La lista con todas las rutas que empiecen con la ciudad con codigoOrigen y termien en la ciudad con codigoDestino
     */
    public ArrayList<ItinerarioRuta> buscarRutasPorDestino( int codigoDestino, ArrayList<ItinerarioRuta> rutasActuales )
    {
        ArrayList<ItinerarioRuta> solucion = new ArrayList<ItinerarioRuta>( );
        for( int i = 0; i < rutasActuales.size( ); i++ )
        {
            Ciudad destino = rutas[ i ].getCiudadDestino( );

            if( destino.getCodigo( ) == codigoDestino )
                solucion.add( rutas[ i ] );
        }
        return solucion;
    }
    /**
     * Metodo que retorna una ciudad que tenga el codigo que entra como parametro
     * @param codigo El codigo de la ciudad para buscar
     * @return La ciudad que cumpla ciudad.codigo = codigo, null en caso de no encontrarla
     */
    public Ciudad buscarCiudadPorCodigo( int codigo )
    {
        for( int i = 0; i < ciudades.length; i++ )
        {
            Ciudad actual = ciudades[ i ];
            if( actual.getCodigo( ) == codigo )
                return actual;
        }
        return null;
    }
    /**
     * Metodo que calcula el tiempo de viaje total para cada una de las rutas posibles
     * <b> ctx: </b> origen es un numero natural. Los elementos de la lista de rutas pertenecen a ItinerarioRuta
     * <b> pre: </b> actual pertenece a ItinerarioRuta y cOrigen pertenece a Ciudad
     * <b> post: </b> Se agregaron las rutas validas a la lista solucion, y se retorno esta lista
     * @param origen La ciudad origen desde donde partira la ruta
     * @param rutasPosibles La lista de todas las rutas que tienen como destino la ciudad destino de la ruta propuesta
     */
    public ArrayList<ItinerarioRuta> calcularTiempoViaje( int origen, ArrayList<ItinerarioRuta> rutasPosibles )
    {
        Ciudad cOrigen = buscarCiudadPorCodigo( origen );
        int tOrigen = cOrigen.getTiempoEspera( );

        for( int i = 0; i < rutasPosibles.size( ); i++ )
        {
            ItinerarioRuta actual = rutasPosibles.get( i );
            actual.sumarTiempo( tOrigen );
        }
        
        return rutasPosibles;
    }
    /**
     * Metodo que determina la hora de salida de las rutas que entran como parametros, basado en la hora y minutos que entran como parametros
     * <b> ctx: </b> 0<=hora<=23, 0<=minutos<=59. Los elementos de la lista de rutas pertenecen a ItinerarioRuta.
     * <b> pre: </b> rutasPosibles.get(i) pertenece a ItinerarioRuta, origen pertenece a Ciudad.
     * <b> post: </b> Se agregaron las rutas validas a la lista solucion, y se retorno esta lista
     * @param hora La hora deseada de salida de la ruta
     * @param minutos Los minutos deseados de salida de la ruta
     * @param rutasPosibles Las rutas posibles de la red de transporte
     * @return rutasPosibles Las rutas posibles de la red de transporte con la hora de salida determinada
     */
    public ArrayList<ItinerarioRuta> determinarHoraPartidaRuta ( int hora, int minutos, ArrayList<ItinerarioRuta> rutasPosibles )
    {
        String horaSalida = "" + hora + Ciudad.SEPARADOR + minutos;
        for (int i = 0; i < rutasPosibles.size( ); i++)
        {
            Ciudad origen = rutasPosibles.get( i ).getCiudadOrigen( );
            if ( origen.existeHoraBase( horaSalida ) )
                rutasPosibles.get( i ).setHoraSalida( horaSalida );
            else
            {
                rutasPosibles.get(i).determinarHoraPartidaRuta ( hora, minutos );
            }
        }
        return rutasPosibles;
    }
    /**
     * Metodo que asigna la hora de salida deseada de las rutas de la red de transporte
     * @param hora La hora deseada de salida de las rutas
     * @param minutos Los minutos deseados de salida de las rutas
     * @param rutasPosibles Las rutas posibles de la red de transporte
     */
    public void setHoraSalidaDeseadaRutas( int hora, int minutos, ArrayList<ItinerarioRuta> rutasPosibles )
    {
        String horaDeseada = "" + hora + Ciudad.SEPARADOR + minutos;
        
        for( Iterator<ItinerarioRuta> iterator = rutasPosibles.iterator( ); iterator.hasNext( ); )
        {
            ItinerarioRuta actual = ( ItinerarioRuta )iterator.next( );
            actual.setHoraSalidaDeseada( horaDeseada );   
        }
    }
    // ---------------------------------------------------------------------------
    // METODO MAIN
    // ---------------------------------------------------------------------------
    /**
     * Metodo main del proyecto
     * @param args
     */
    public static void main( String[] args )
    {
        try
        {
            new Lector( );
        }
        catch( Exception e )
        {
            e.printStackTrace( );
        }
    }
}
